"""
复合元数据提取器

结合FFmpeg和OpenCV的优势，提供最全面的元数据提取。
"""

import asyncio
from typing import Dict, Any, Union, List
import logging

from src.core.di import Injectable, Inject
from .base import MetadataExtractor, VideoMetadata, AudioMetadata
from .ffmpeg_extractor import FFmpegMetadataExtractor
from .opencv_extractor import OpenCVMetadataExtractor


@Injectable(scope="singleton")
class CompositeMetadataExtractor(MetadataExtractor):
    """
    复合元数据提取器
    
    结合FFmpeg和OpenCV提取器，提供最全面的元数据信息。
    """
    
    def __init__(self,
                 config: Dict[str, Any] = Inject("config"),
                 logger: logging.Logger = Inject("logger"),
                 ffmpeg_extractor: FFmpegMetadataExtractor = Inject(FFmpegMetadataExtractor),
                 opencv_extractor: OpenCVMetadataExtractor = Inject(OpenCVMetadataExtractor)):
        super().__init__()
        self.config = config
        self.logger = logger
        self.ffmpeg_extractor = ffmpeg_extractor
        self.opencv_extractor = opencv_extractor
        
        # 配置
        self.composite_config = config.get("metadata", {})
        self.enable_opencv = self.composite_config.get("enable_opencv_features", True)
        self.parallel_extraction = self.composite_config.get("parallel_extraction", True)
    
    def get_supported_formats(self) -> List[str]:
        """获取支持的文件格式"""
        # 合并两个提取器支持的格式
        ffmpeg_formats = set(self.ffmpeg_extractor.get_supported_formats())
        opencv_formats = set(self.opencv_extractor.get_supported_formats())
        return list(ffmpeg_formats.union(opencv_formats))
    
    async def extract_metadata(self, file_path: str, **kwargs) -> Union[VideoMetadata, AudioMetadata]:
        """提取文件元数据"""
        file_type = self.get_file_type(file_path)
        
        if file_type == "video":
            return await self.extract_video_metadata(file_path, **kwargs)
        elif file_type == "audio":
            return await self.extract_audio_metadata(file_path, **kwargs)
        else:
            # 尝试作为视频文件处理
            try:
                return await self.extract_video_metadata(file_path, **kwargs)
            except Exception:
                # 如果失败，尝试作为音频文件处理
                return await self.extract_audio_metadata(file_path, **kwargs)
    
    async def extract_video_metadata(self, video_path: str, **kwargs) -> VideoMetadata:
        """提取视频元数据"""
        self.logger.info(f"使用复合提取器提取视频元数据: {video_path}")
        
        # 决定是否启用OpenCV特征提取
        enable_opencv = kwargs.get("enable_opencv", self.enable_opencv)
        
        if self.parallel_extraction and enable_opencv:
            # 并行提取
            return await self._extract_video_metadata_parallel(video_path, **kwargs)
        else:
            # 串行提取
            return await self._extract_video_metadata_sequential(video_path, enable_opencv, **kwargs)
    
    async def _extract_video_metadata_parallel(self, video_path: str, **kwargs) -> VideoMetadata:
        """并行提取视频元数据"""
        self.logger.debug("并行提取视频元数据")
        
        # 创建并行任务
        tasks = [
            asyncio.create_task(self.ffmpeg_extractor.extract_video_metadata(video_path, **kwargs)),
        ]
        
        # 如果启用OpenCV，添加OpenCV任务
        if self.enable_opencv:
            tasks.append(
                asyncio.create_task(self.opencv_extractor.extract_video_metadata(video_path, **kwargs))
            )
        
        # 等待所有任务完成
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        # 处理结果
        ffmpeg_metadata = None
        opencv_metadata = None
        
        if len(results) >= 1 and not isinstance(results[0], Exception):
            ffmpeg_metadata = results[0]
        
        if len(results) >= 2 and not isinstance(results[1], Exception):
            opencv_metadata = results[1]
        
        # 合并元数据
        return self._merge_video_metadata(ffmpeg_metadata, opencv_metadata, video_path)
    
    async def _extract_video_metadata_sequential(self, video_path: str, enable_opencv: bool, **kwargs) -> VideoMetadata:
        """串行提取视频元数据"""
        self.logger.debug("串行提取视频元数据")
        
        # 首先使用FFmpeg提取基础元数据
        ffmpeg_metadata = None
        try:
            ffmpeg_metadata = await self.ffmpeg_extractor.extract_video_metadata(video_path, **kwargs)
        except Exception as e:
            self.logger.warning(f"FFmpeg元数据提取失败: {e}")
        
        # 如果启用，使用OpenCV提取视觉特征
        opencv_metadata = None
        if enable_opencv:
            try:
                opencv_metadata = await self.opencv_extractor.extract_video_metadata(video_path, **kwargs)
            except Exception as e:
                self.logger.warning(f"OpenCV元数据提取失败: {e}")
        
        # 合并元数据
        return self._merge_video_metadata(ffmpeg_metadata, opencv_metadata, video_path)
    
    def _merge_video_metadata(self, ffmpeg_metadata: VideoMetadata, opencv_metadata: VideoMetadata, video_path: str) -> VideoMetadata:
        """合并视频元数据"""
        # 以FFmpeg元数据为基础
        if ffmpeg_metadata:
            merged = ffmpeg_metadata
        elif opencv_metadata:
            merged = opencv_metadata
        else:
            # 如果都失败了，创建基础元数据
            from pathlib import Path
            import os
            from datetime import datetime
            
            path_obj = Path(video_path)
            file_stat = os.stat(video_path)
            
            merged = VideoMetadata(
                filename=path_obj.name,
                filepath=str(path_obj.absolute()),
                file_size=file_stat.st_size,
                modification_time=datetime.fromtimestamp(file_stat.st_mtime)
            )
        
        # 合并OpenCV的视觉特征
        if opencv_metadata and opencv_metadata.custom_metadata:
            # 如果FFmpeg没有提供某些基础信息，使用OpenCV的
            if not merged.width and opencv_metadata.width:
                merged.width = opencv_metadata.width
            if not merged.height and opencv_metadata.height:
                merged.height = opencv_metadata.height
            if not merged.fps and opencv_metadata.fps:
                merged.fps = opencv_metadata.fps
            if not merged.frame_count and opencv_metadata.frame_count:
                merged.frame_count = opencv_metadata.frame_count
            if not merged.duration and opencv_metadata.duration:
                merged.duration = opencv_metadata.duration
            if not merged.aspect_ratio and opencv_metadata.aspect_ratio:
                merged.aspect_ratio = opencv_metadata.aspect_ratio
            
            # 合并自定义元数据
            opencv_features = opencv_metadata.custom_metadata
            merged.custom_metadata.update({
                "opencv_features": opencv_features,
                "composite_extraction": True,
                "extractors_used": ["ffmpeg", "opencv"] if ffmpeg_metadata else ["opencv"]
            })
        else:
            merged.custom_metadata.update({
                "composite_extraction": True,
                "extractors_used": ["ffmpeg"] if ffmpeg_metadata else []
            })
        
        self.logger.info(f"复合元数据提取完成: {merged.filename}")
        return merged
    
    async def extract_audio_metadata(self, audio_path: str, **kwargs) -> AudioMetadata:
        """提取音频元数据"""
        self.logger.info(f"提取音频元数据: {audio_path}")
        
        # 音频文件只使用FFmpeg提取器
        try:
            metadata = await self.ffmpeg_extractor.extract_audio_metadata(audio_path, **kwargs)
            metadata.custom_metadata.update({
                "composite_extraction": True,
                "extractors_used": ["ffmpeg"]
            })
            return metadata
        except Exception as e:
            self.logger.error(f"音频元数据提取失败: {e}")
            raise
    
    async def batch_extract_metadata(self, file_paths: List[str], **kwargs) -> Dict[str, Union[VideoMetadata, AudioMetadata]]:
        """批量提取元数据"""
        self.logger.info(f"批量提取 {len(file_paths)} 个文件的元数据")
        
        results = {}
        
        # 创建并行任务
        tasks = []
        for file_path in file_paths:
            task = asyncio.create_task(self.extract_metadata(file_path, **kwargs))
            tasks.append((file_path, task))
        
        # 等待所有任务完成
        for file_path, task in tasks:
            try:
                metadata = await task
                results[file_path] = metadata
            except Exception as e:
                self.logger.error(f"提取 {file_path} 元数据失败: {e}")
                results[file_path] = None
        
        self.logger.info(f"批量元数据提取完成: 成功 {len([r for r in results.values() if r])}/{len(file_paths)}")
        return results
    
    # 实现插件接口方法
    async def classify(self, input_data: Any, **kwargs) -> Any:
        """分类数据（不适用于元数据提取器）"""
        raise NotImplementedError("元数据提取器不支持分类功能")
    
    async def output(self, data: Any, **kwargs) -> Any:
        """输出数据（不适用于元数据提取器）"""
        raise NotImplementedError("元数据提取器不支持输出功能")
